﻿<template>
  <div class="timeline-root" ref="timelineContainer" @scroll.passive="onScroll">
    <!-- 筛选栏 -->
    <form class="filters" @submit.prevent>
      <select v-model="filter.type" @change="doFilter">
        <option value="">全部类型</option>
        <option v-for="(label, val) in typeOptions" :key="val" :value="val">{{ label }}</option>
      </select>
      <select v-model="filter.codeLanguage" @change="doFilter">
        <option value="">全部语言</option>
        <option v-for="(label, val) in languageOptions" :key="val" :value="val">{{ label }}</option>
      </select>
      <select v-model="filter.isCode" @change="doFilter">
        <option value="">全部</option>
        <option :value="true">仅代码</option>
        <option :value="false">非代码</option>
      </select>
      <select v-model="datePreset" @change="onDatePresetChange" style="min-width:100px;">
        <option value="">全部日期</option>
        <option value="today">今天</option>
        <option value="yesterday">昨天</option>
        <option value="lastweek">上周</option>
        <option value="range">日期范围</option>
      </select>
      <input
        v-if="datePreset==='range'"
        type="date"
        v-model="filter.from"
        @change="doFilter"
        style="max-width:110px"
        :max="filter.to || undefined"
        placeholder="开始"
      />
      <span v-if="datePreset==='range'" style="margin:0 4px;">~</span>
      <input
        v-if="datePreset==='range'"
        type="date"
        v-model="filter.to"
        @change="doFilter"
        style="max-width:110px"
        :min="filter.from || undefined"
        placeholder="结束"
      />
      <input v-model="filter.keywords" placeholder="关键词" @input="doFilter" />
      <button class="btn-gray" type="button" @click="resetFilter">重置</button>
    </form>
    <!-- 日期锚点 -->
    <div class="timeline-nav" v-if="days.length > 2">
      <span v-for="day in days" :key="day" class="nav-day" @click="scrollToDay(day)">
        {{ day }}
      </span>
    </div>
    <!-- 内容区 -->
    <div class="timeline-list">
      <template v-if="groupedLogs.length > 0">
        <div v-for="group in groupedLogs" :key="group.day" :ref="el => setDayRef(group.day, el)">
          <div class="day-title">{{ group.day }}</div>
          <div
            v-for="item in group.items"
            :key="item.id"
            class="timeline-item"
          >
            <div class="item-header">
              <span class="time">{{ formatTime(item.createdAt) }}</span>
              <span class="type" :class="['type-' + item.type]">{{ typeOptions[item.type] || item.type }}</span>
              <span v-if="item.isCode" class="pill code-icon">代码</span>
            </div>
            <!-- 操作按钮区 -->
            <button
              v-if="item.content.length <= 199"
              class="copy-btn-top"
              title="复制"
              @click.stop="copyContent(item.content)"
              type="button"
            >
              📋 复制
            </button>
            <div class="item-content" :class="{ 'is-code': item.isCode }">
              <template v-if="item.isCode">
                <pre class="tight-pre">
                  <code v-html="highlightCode(item.content, item.codeLanguage)"></code>
                </pre>
                <template v-if="item.content.length >= 199">
                  <button
                    v-if="!item.showFull"
                    class="show-full-btn-corner"
                    @click.stop="showFullContent(item)"
                    type="button"
                    title="显示完整内容"
                  >显示全部</button>
                  <button
                    v-else
                    class="copy-btn-top copy-btn-inline"
                    @click.stop="copyContent(item.content)"
                    type="button"
                    title="复制"
                  >📋 复制</button>
                </template>
              </template>
              <template v-else>
                <div class="plain-content">
                  <span>{{ item.content }}</span>
                  <template v-if="item.content.length >= 199">
                    <button
                      v-if="!item.showFull"
                      class="show-full-btn-corner"
                      @click.stop="showFullContent(item)"
                      type="button"
                      title="显示完整内容"
                    >显示全部</button>
                    <button
                      v-else
                      class="copy-btn-top copy-btn-inline"
                      @click.stop="copyContent(item.content)"
                      type="button"
                      title="复制"
                    >📋 复制</button>
                  </template>
                </div>
              </template>
            </div>
          </div>
        </div>
      </template>
      <div v-else class="empty-tip">
        <span>暂无内容，试试筛选或复制新内容吧~</span>
      </div>
      <div v-if="loading" class="loading">加载中...</div>
      <div v-if="noMore && logs.length" class="nomore">没有更多了</div>
    </div>
    <!-- 复制提示 -->
    <transition name="fade">
      <div v-if="copiedTip" class="copied-tip">{{ copiedTip }}</div>
    </transition>
    <!-- 完整内容弹窗 -->
    <div v-if="showFullDialog" class="full-dialog-bg" @click.self="closeFullDialog">
      <div class="full-dialog light-theme">
        <h3>完整内容</h3>
        <div v-if="isFullContentCode" class="full-content-codewrap">
          <pre class="tight-pre"><code v-html="highlightCode(fullContent, fullContentLang)"></code></pre>
        </div>
        <div v-else class="full-content-textwrap">
          <div class="full-content-text" v-text="fullContent"></div>
        </div>
        <div class="dialog-btns-center">
          <button class="copy-btn-top no-break" @click="copyContent(fullContent)" type="button" title="复制">
            📋 复制
          </button>
          <button class="copy-btn-top btn-gray no-break" @click="closeFullDialog" type="button" title="关闭">
            关闭
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, nextTick, watch } from 'vue'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css' // 浅色高亮风格

interface ClipboardLog {
  id: number
  content: string
  type: number
  codeLanguage?: number
  isCode?: boolean
  createdAt: string
  showFull?: boolean // 前端标识
}

const logs = ref<ClipboardLog[]>([])
const loading = ref(false)
const noMore = ref(false)
const page = ref(1)
const pageSize = ref(30)
const total = ref(0)

const timelineContainer = ref<HTMLElement | null>(null)
const dayRefs = ref<Record<string, HTMLElement>>({})
function setDayRef(day: string, el: HTMLElement | null) {
  if (el) dayRefs.value[day] = el
}
const days = computed(() => groupedLogs.value.map(g => g.day))

const groupedLogs = computed(() => {
  const groups: Record<string, ClipboardLog[]> = {}
  for (const log of logs.value) {
    const day = log.createdAt.substring(0, 10)
    if (!groups[day]) groups[day] = []
    groups[day].push(log)
  }
  return Object.entries(groups)
    .sort((a, b) => b[0].localeCompare(a[0]))
    .map(([day, items]) => ({ day, items }))
})

const filter = ref({
  type: '',
  codeLanguage: '',
  isCode: '',
  keywords: '',
  from: '',
  to: ''
})

const typeOptions: Record<number, string> = {
  1: '文本', 2: '网址', 3: '邮箱', 4: '代码', 5: '图片', 6: '文件'
}
const languageOptions: Record<number, string> = {
  1: 'C#', 2: 'Java', 3: 'Python', 4: 'JavaScript',
  5: 'TypeScript', 6: 'Go', 7: 'C++', 8: 'SQL', 9: 'HTML',
    10: 'Shell', 11: 'Vue'
}
const languageMap: Record<number, string> = {
  1: 'csharp', 2: 'java', 3: 'python', 4: 'javascript',
  5: 'typescript', 6: 'go', 7: 'cpp', 8: 'sql', 9: 'xml',
    10: 'shell', 11: 'Vue'
}
function highlightCode(code: string, langId?: number) {
  const lang = langId ? (languageMap[langId] || 'plaintext') : 'plaintext'
  try {
    return hljs.highlight(code, { language: lang }).value
  } catch {
    return hljs.highlightAuto(code).value
  }
}
function formatTime(dt: string) {
  return dt.substring(11, 19)
}

// 弹窗内容类型判断
const showFullDialog = ref(false)
const fullContent = ref('')
const fullContentLang = ref<number | undefined>(undefined)
const isFullContentCode = ref(false)
async function showFullContent(item: ClipboardLog) {
  // 调用接口获取完整内容
  const res = await fetch(`http://localhost:5000/clipboard/full?id=${item.id}`)
  const data = await res.json()
  if (data && data.content) {
    fullContent.value = data.content
    fullContentLang.value = item.codeLanguage
    isFullContentCode.value = !!item.isCode
    //showFullDialog.value = true
    item.content = data.content
    item.showFull = true
  }
}
function closeFullDialog() {
  showFullDialog.value = false
  fullContent.value = ''
  fullContentLang.value = undefined
  isFullContentCode.value = false
}

// 日期筛选相关
const datePreset = ref<'today'|'yesterday'|'lastweek'|'range'|''>('')
function onDatePresetChange() {
  const today = new Date()
  const yyyy = today.getFullYear(), mm = String(today.getMonth() + 1).padStart(2, '0'), dd = String(today.getDate()).padStart(2, '0')
  if (datePreset.value === 'today') {
    filter.value.from = filter.value.to = `${yyyy}-${mm}-${dd}`
    doFilter()
  } else if (datePreset.value === 'yesterday') {
    const y = new Date(today)
    y.setDate(today.getDate() - 1)
    const yyyy2 = y.getFullYear(), mm2 = String(y.getMonth() + 1).padStart(2, '0'), dd2 = String(y.getDate()).padStart(2, '0')
    filter.value.from = filter.value.to = `${yyyy2}-${mm2}-${dd2}`
    doFilter()
  } else if (datePreset.value === 'lastweek') {
    // 获取上周一和上周日
    const now = new Date(today)
    const dayOfWeek = now.getDay()
    const monday = new Date(today)
    monday.setDate(today.getDate() - (dayOfWeek === 0 ? 6 : dayOfWeek - 1))
    const lastMonday = new Date(monday)
    lastMonday.setDate(monday.getDate() - 7)
    const lastSunday = new Date(lastMonday)
    lastSunday.setDate(lastMonday.getDate() + 6)
    const y1 = lastMonday.getFullYear(), m1 = String(lastMonday.getMonth() + 1).padStart(2, '0'), d1 = String(lastMonday.getDate()).padStart(2, '0')
    const y2 = lastSunday.getFullYear(), m2 = String(lastSunday.getMonth() + 1).padStart(2, '0'), d2 = String(lastSunday.getDate()).padStart(2, '0')
    filter.value.from = `${y1}-${m1}-${d1}`
    filter.value.to = `${y2}-${m2}-${d2}`
    doFilter()
  } else if (datePreset.value === 'range') {
    filter.value.from = ''
    filter.value.to = ''
    // 日期输入控件已watch
  } else {
    filter.value.from = ''
    filter.value.to = ''
    doFilter()
  }
}

// 监听自定义日期范围自动搜索
watch([() => filter.value.from, () => filter.value.to], ([from, to]) => {
  if (datePreset.value === 'range' && from && to) {
    doFilter()
  }
})

async function loadMore(clear = false) {
  if (loading.value) return
  if (!clear && noMore.value) return

  loading.value = true

  if (clear) {
    logs.value = []
    page.value = 1
    noMore.value = false
  }

  const params = new URLSearchParams({
    page: String(page.value),
    pageSize: String(pageSize.value)
  })
  Object.entries(filter.value).forEach(([k, v]) => {
    if (v !== '' && v !== undefined && v !== null) params.append(k, v as string)
  })

  const res = await fetch('http://localhost:5000/clipboard/search?' + params.toString())
  const data = await res.json()
  if (!data.items || data.items.length === 0) {
    if (page.value === 1) {
      logs.value = []
      total.value = 0
    }
    noMore.value = true
  } else {
    logs.value.push(...data.items)
    page.value++
    total.value = data.total
  }
  loading.value = false
  await nextTick()
  document.querySelectorAll('pre code').forEach(block => {
    hljs.highlightElement(block as HTMLElement)
  })
}

function doFilter() {
  loadMore(true)
}
function resetFilter() {
  filter.value = {
    type: '',
    codeLanguage: '',
    isCode: '',
    keywords: '',
    from: '',
    to: ''
  }
  datePreset.value = ''
  loadMore(true)
}

// 无限滚动
function onScroll() {
  if (!timelineContainer.value) return
  const el = timelineContainer.value
  if (el.scrollTop + el.clientHeight + 100 > el.scrollHeight) loadMore()
}

// 日期锚点跳转
function scrollToDay(day: string) {
  const el = dayRefs.value[day]
  if (el && timelineContainer.value) {
    timelineContainer.value.scrollTo({
      top: el.offsetTop - 20,
      behavior: 'smooth'
    })
  }
}

// 复制内容
const copiedTip = ref('')
async function copyContent(text: string) {
  try {
    await navigator.clipboard.writeText(text)
    copiedTip.value = '已复制到剪贴板'
  } catch {
    copiedTip.value = '复制失败，请手动复制'
  }
  setTimeout(() => (copiedTip.value = ''), 1200)
}

onMounted(() => loadMore(true))
</script>

<style scoped>
@import "highlight.js/styles/github.css";

/* 滚动条美化（Webkit） */
.timeline-root, 
.timeline-list, 
.full-dialog, 
.full-dialog pre,
.full-content-textwrap {
  scrollbar-width: thin;
  scrollbar-color: #c1dbfa #e6f7ff;
}
.timeline-root::-webkit-scrollbar,
.timeline-list::-webkit-scrollbar,
.full-dialog::-webkit-scrollbar,
.full-dialog pre::-webkit-scrollbar,
.full-content-textwrap::-webkit-scrollbar {
  width: 8px;
  background: #e6f7ff;
}
.timeline-root::-webkit-scrollbar-thumb,
.timeline-list::-webkit-scrollbar-thumb,
.full-dialog::-webkit-scrollbar-thumb,
.full-dialog pre::-webkit-scrollbar-thumb,
.full-content-textwrap::-webkit-scrollbar-thumb {
  background: #c1dbfa;
  border-radius: 7px;
}
.timeline-root::-webkit-scrollbar-thumb:hover,
.timeline-list::-webkit-scrollbar-thumb:hover,
.full-dialog::-webkit-scrollbar-thumb:hover,
.full-dialog pre::-webkit-scrollbar-thumb:hover,
.full-content-textwrap::-webkit-scrollbar-thumb:hover {
  background: #90b8e8;
}

.full-dialog.light-theme {
  background: #fff;
}
.full-dialog h3 {
  color: #3184e7;
  text-align: center;
}
.full-content-codewrap {
  background: #f7f7fa;
  border-radius: 7px;
  padding: 0;
  margin-bottom: 18px;
  overflow-x: auto;
}
.full-content-codewrap pre {
  background: transparent;
  color: inherit;
  margin: 0;
  padding: 10px 6px;
  border-radius: 7px;
  font-size: 15px;
  max-width: 80vw;
  max-height: 44vh;
  word-break: break-all;
  overflow: auto;
  line-height: 1.5;
}
.full-content-textwrap {
  background: #f7f7fa;
  border-radius: 7px;
  padding: 12px 11px;
  margin-bottom: 18px;
  max-width: 80vw;
  max-height: 44vh;
  overflow: auto;
  color: #222;
}
.full-content-text {
  font-size: 15px;
  line-height: 1.7;
  white-space: pre-wrap;
  word-break: break-all;
}
.dialog-btns-center {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 28px;
  margin: 18px 0 0 0;
  flex-wrap: nowrap;
}
.full-dialog .copy-btn-top {
  background: #ecf3fd;
  color: #3184e7;
  border: 1px solid #d1e7fb;
  white-space: nowrap;
  flex-shrink: 0;
  flex-grow: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
}
.full-dialog .copy-btn-top.btn-gray {
  background: #eaeaea;
  color: #444;
  border: 1px solid #ccc;
}
.full-dialog .copy-btn-top:hover {
  background: #d3eaff;
  color: #1956b3;
}
.full-dialog .copy-btn-top.no-break {
  white-space: nowrap;
  flex-shrink: 0;
  flex-grow: 0;
}
.timeline-root {
  max-width: 90%;
  margin: 0 auto;
  padding: 8px 2vw 28px 2vw;
  height: 100vh;
  box-sizing: border-box;
  background: #f8fafb;
  overflow-y: auto;
  position: relative;
}
@media (max-width: 660px) {
  .timeline-root {
    max-width: 100vw;
    padding: 4px 0vw 60px 0vw;
    border-radius: 0;
  }
}
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 8px;
  align-items: center;
  margin: 8px 0 16px 0;
  background: #fff;
  padding: 7px 7px 7px 12px;
  border-radius: 7px;
  box-shadow: 0 1px 5px #0001;
}
.filters input,
.filters select {
  padding: 5px 10px;
  border: 1px solid #d8e0eb;
  border-radius: 4px;
  font-size: 15px;
  outline: none;
  background: #f7f7fb;
  min-width: 90px;
  flex: 1 1 120px;
}
.filters select {
  min-width: 65px;
}
.filters button {
  height: 32px;
  font-size: 15px;
  min-width: 58px;
  border-radius: 4px;
  margin-left: 7px;
  margin-right: 4px;
  cursor: pointer;
  border: none;
}
.btn-main {
  background: linear-gradient(90deg, #3184e7 50%, #0fbecf 100%);
  color: #fff;
}
.btn-gray {
  background: #eaeaea;
  color: #444;
}
.timeline-nav {
  overflow-x: auto;
  white-space: nowrap;
  margin-bottom: 8px;
  padding-bottom: 2px;
  -webkit-overflow-scrolling: touch;
}
.timeline-nav .nav-day {
  display: inline-block;
  background: #f7faff;
  border: 1px solid #c1dbfa;
  border-radius: 13px;
  padding: 3px 14px;
  font-size: 14px;
  margin-right: 8px;
  cursor: pointer;
  margin-bottom: 3px;
  transition: background 0.15s;
}
.timeline-nav .nav-day:hover {
  background: #e6f7ff;
}
.day-title {
  font-weight: bold;
  font-size: 17px;
  margin: 18px 0 6px 0;
  color: #3a7eda;
  letter-spacing: 1.5px;
}
.timeline-item {
  background: #fff;
  border-radius: 8px;
  margin-bottom: 10px;
  box-shadow: 0 1px 3px #0001;
  padding: 10px 12px 8px 14px;
  transition: box-shadow 0.14s, border 0.14s;
  position: relative;
}
.timeline-item:hover {
  box-shadow: 0 3px 12px #0002;
  border: 1.5px solid #a7d2f8;
}
.item-header {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #90a;
  font-size: 13px;
  margin-bottom: 2px;
  flex-wrap: wrap;
}
.type-4 { color: #3184e7; font-weight: bold; }
.pill {
  border-radius: 3px;
  padding: 0 6px;
  background: #f4f4ff;
  color: #3b4b8d;
  font-size: 12px;
  margin-left: 2px;
}
.code-icon {
  background: #eaf0fd;
  color: #1956b3;
}
.item-content {
  font-size: 16px;
  margin-left: 1px;
  margin-top: 2px;
  word-break: break-all;
  line-height: 1.7;
  position: relative;
}
.item-content.is-code {
  background: #f7f7fa;
  border-radius: 7px;
  padding: 4px 4px 2px 6px;
}
.tight-pre {
  margin: 0;
  padding: 2px 0 2px 0;
  font-size: 15px;
  background: none;
  border-radius: 0;
  overflow-x: auto;
  line-height: 1.5;
}
.item-content pre code {
  font-size: 15px;
  padding: 0;
  margin: 0;
  background: none;
  line-height: 1.5;
}
.plain-content {
  padding: 2px 0;
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 0 6px;
  position: relative;
}
.copy-btn-top {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  background: #ecf3fd;
  color: #3184e7;
  border: 1px solid #d1e7fb;
  border-radius: 7px;
  font-size: 15px;
  width: 68px;
  height: 30px;
  line-height: 28px;
  padding: 0 10px;
  text-align: center;
  cursor: pointer;
  transition: background 0.15s;
  white-space: nowrap;
  flex-shrink: 0;
  flex-grow: 0;
}
.copy-btn-top:hover {
  background: #d3eaff;
  color: #1956b3;
}
.timeline-item .copy-btn-top {
  position: absolute;
  right: 12px;
  top: 8px;
  z-index: 12;
}
.copy-btn-inline {
  /* 右下角复制按钮样式与 .copy-btn-top 一致 */
  position: absolute;
  right: 8px;
  bottom: 5px;
  box-shadow: none;
  margin: 0;
  border-radius: 7px;
  width: 68px;
  height: 28px;
  font-size: 14px;
  background: #ecf3fd;
  color: #3184e7;
  border: 1px solid #d1e7fb;
  opacity: 0.98;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  white-space: nowrap;
  flex-shrink: 0;
  flex-grow: 0;
  transition: background 0.15s;
}
.copy-btn-inline:hover {
  background: #d3eaff;
  color: #1956b3;
}
.show-full-btn-corner {
  position: absolute;
  right: 8px;
  bottom: 5px;
  font-size: 13px;
  color: #fff;
  background: #3184e7;
  border: none;
  border-radius: 7px;
  padding: 3px 13px 3px 13px;
  cursor: pointer;
  outline: none;
  display: flex;
  align-items: center;
  gap: 2px;
  opacity: 0.98;
  white-space: nowrap;
}
.show-full-btn-corner:hover {
  background: #1956b3;
  color: #fff;
}
.loading, .nomore, .empty-tip {
  text-align: center;
  color: #aaa;
  padding: 13px 0 5px 0;
  font-size: 16px;
}
.copied-tip {
  position: fixed;
  left: 50%;
  bottom: 33px;
  transform: translateX(-50%);
  background: #3184e7;
  color: #fff;
  font-size: 15px;
  padding: 9px 32px;
  border-radius: 22px;
  box-shadow: 0 2px 15px #0002;
  opacity: 0.96;
  z-index: 1000;
}
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.36s;
}
.fade-enter-from, .fade-leave-to {
  opacity: 0;
}
.full-dialog-bg {
  position: fixed;
  z-index: 3000;
  left: 0; top: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.35);
  display: flex;
  align-items: center;
  justify-content: center;
}
.full-dialog {
  border-radius: 8px;
  min-width: 300px;
  max-width: 96vw;
  max-height: 80vh;
  overflow: auto;
  box-shadow: 0 7px 28px #0002;
  padding: 28px 20px 18px 20px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  background: #fff;
}
.full-dialog h3 {
  margin: 0 0 18px 0;
  font-size: 19px;
  color: #3184e7;
  text-align: center;
}
</style>